<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>XML格式化与压缩工具</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        :root {
            --primary-color: #1a73e8;
            --secondary-color: #34a853;
            --accent-color: #fbbc05;
            --danger-color: #ea4335;
            --dark-color: #202124;
            --light-color: #f8f9fa;
            --bg-gradient-start: #6ecbf5;
            --bg-gradient-end: #e6f7ff;
            --panel-bg: rgba(255, 255, 255, 0.92);
            --border-color: rgba(0, 0, 0, 0.1);
        }

        body {
            font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
            background: linear-gradient(135deg, var(--bg-gradient-start), var(--bg-gradient-end));
            color: var(--dark-color);
            min-height: 100vh;
            padding: 20px;
            line-height: 1.6;
        }

        .container {
            max-width: 1400px;
            margin: 0 auto;
            display: flex;
            flex-direction: column;
            gap: 25px;
        }

        header {
            text-align: center;
            padding: 25px 30px;
            background: var(--panel-bg);
            border-radius: 16px;
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
            backdrop-filter: blur(10px);
            border: 1px solid var(--border-color);
            animation: fadeInDown 0.8s ease;
        }

        h1 {
            font-size: 2.8rem;
            margin-bottom: 10px;
            color: var(--primary-color);
            background: linear-gradient(135deg, var(--primary-color), #00c6ff);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
        }

        .subtitle {
            font-size: 1.2rem;
            color: #5f6368;
            max-width: 800px;
            margin: 15px auto;
            line-height: 1.7;
        }

        .panel-container {
            display: flex;
            flex-direction: column;
            gap: 25px;
        }

        .input-section, .output-section {
            background: var(--panel-bg);
            border-radius: 16px;
            overflow: hidden;
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
            backdrop-filter: blur(10px);
            border: 1px solid var(--border-color);
            animation: fadeInUp 0.8s ease 0.2s both;
        }

        .section-header {
            background: linear-gradient(to right, var(--primary-color), #4a90e2);
            color: white;
            padding: 16px 25px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-weight: 600;
            font-size: 1.25rem;
        }

        .section-tools {
            display: flex;
            gap: 12px;
        }

        .tool-btn {
            background: rgba(255, 255, 255, 0.2);
            color: white;
            border: none;
            width: 36px;
            height: 36px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: all 0.3s;
            font-size: 16px;
        }

        .tool-btn:hover {
            background: rgba(255, 255, 255, 0.3);
            transform: scale(1.1);
        }

        .tool-btn:active {
            transform: scale(0.95);
        }

        .input-area, .output-area {
            padding: 25px;
            position: relative;
        }

        textarea {
            width: 100%;
            min-height: 250px;
            padding: 20px;
            border: 1px solid #dadce0;
            border-radius: 12px;
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 1.05rem;
            resize: vertical;
            background: rgba(255, 255, 255, 0.95);
            box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.06);
            line-height: 1.7;
            color: #202124;
            transition: all 0.3s;
        }

        textarea:focus {
            outline: none;
            border-color: var(--primary-color);
            box-shadow: 0 0 0 2px rgba(26, 115, 232, 0.2),
                        inset 0 2px 8px rgba(0, 0, 0, 0.06);
        }

        .controls {
            display: flex;
            gap: 15px;
            margin-top: 20px;
            flex-wrap: wrap;
        }

        .action-btn {
            padding: 14px 24px;
            border: none;
            border-radius: 12px;
            background: var(--primary-color);
            color: white;
            font-weight: 600;
            font-size: 1.05rem;
            cursor: pointer;
            transition: all 0.3s;
            flex: 1;
            min-width: 160px;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            box-shadow: 0 4px 12px rgba(26, 115, 232, 0.25);
        }

        .action-btn:hover {
            transform: translateY(-3px);
            box-shadow: 0 6px 16px rgba(26, 115, 232, 0.35);
        }

        .action-btn:active {
            transform: translateY(1px);
        }

        #formatBtn {
            background: var(--secondary-color);
            box-shadow: 0 4px 12px rgba(52, 168, 83, 0.25);
        }

        #formatBtn:hover {
            box-shadow: 0 6px 16px rgba(52, 168, 83, 0.35);
        }

        #compressBtn {
            background: #673ab7;
            box-shadow: 0 4px 12px rgba(103, 58, 183, 0.25);
        }

        #compressBtn:hover {
            box-shadow: 0 6px 16px rgba(103, 58, 183, 0.35);
        }

        #restoreBtn {
            background: #ff9800;
            box-shadow: 0 4px 12px rgba(255, 152, 0, 0.25);
        }

        #restoreBtn:hover {
            box-shadow: 0 6px 16px rgba(255, 152, 0, 0.35);
        }

        #clearBtn {
            background: var(--danger-color);
            box-shadow: 0 4px 12px rgba(234, 67, 53, 0.25);
        }

        #clearBtn:hover {
            box-shadow: 0 6px 16px rgba(234, 67, 53, 0.35);
        }

        #formattedOutput {
            font-family: 'Consolas', 'Courier New', monospace;
            font-size: 1.05rem;
            line-height: 1.7;
            word-wrap: break-word;
            white-space: pre-wrap;
            padding: 20px;
            border-radius: 12px;
            background: #f8fbff;
            border: 1px solid #e3eef9;
            min-height: 300px;
            max-height: 600px;
            overflow: auto;
            box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.04);
        }

        /* XML元素样式 */
        .element {
            margin: 10px 0;
            padding-left: 28px;
            position: relative;
        }

        .element.collapsible {
            cursor: pointer;
        }

        .element.collapsible::before {
            content: '▶';
            position: absolute;
            left: 6px;
            top: 3px;
            color: var(--primary-color);
            font-size: 0.9rem;
        }

        .element.collapsed::before {
            content: '▼';
        }

        .tag {
            color: #0d47a1;
            font-weight: bold;
        }

        .attribute-name {
            color: #d32f2f;
            font-weight: 600;
        }

        .attribute-value {
            color: #388e3c;
        }

        .text-content {
            color: #5f6368;
            word-break: break-word;
            padding: 3px 0;
        }

        .comment {
            color: #7f7f7f;
            font-style: italic;
        }

        .single-line-element {
            display: inline-block;
            margin: 7px 0;
        }

        .footer {
            text-align: center;
            padding: 25px;
            color: #5f6368;
            font-size: 1rem;
            background: var(--panel-bg);
            border-radius: 16px;
            backdrop-filter: blur(10px);
            box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
            border: 1px solid var(--border-color);
            animation: fadeIn 0.8s ease 0.4s both;
        }

        .footer a {
            color: var(--primary-color);
            text-decoration: none;
            font-weight: 600;
        }

        .footer a:hover {
            text-decoration: underline;
        }

        /* 动画效果 */
        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        @keyframes fadeInDown {
            from { opacity: 0; transform: translateY(-20px); }
            to { opacity: 1; transform: translateY(0); }
        }

        @keyframes fadeInUp {
            from { opacity: 0; transform: translateY(20px); }
            to { opacity: 1; transform: translateY(0); }
        }

        @keyframes pulse {
            0% { transform: scale(1); }
            50% { transform: scale(1.05); }
            100% { transform: scale(1); }
        }

        /* 响应式设计 */
        @media (max-width: 992px) {
            .container {
                gap: 18px;
            }

            header {
                padding: 20px;
            }

            h1 {
                font-size: 2.4rem;
            }

            .subtitle {
                font-size: 1.1rem;
            }

            .input-area, .output-area {
                padding: 20px;
            }

            textarea {
                min-height: 200px;
                padding: 16px;
            }

            .action-btn {
                min-width: 140px;
                padding: 12px 20px;
                font-size: 1rem;
            }
        }

        @media (max-width: 768px) {
            h1 {
                font-size: 2rem;
            }

            .subtitle {
                font-size: 1rem;
            }

            .section-header {
                padding: 14px 20px;
                font-size: 1.15rem;
            }

            .action-btn {
                flex-basis: 100%;
            }

            .footer {
                padding: 20px;
            }
        }

        @media (max-width: 480px) {
            body {
                padding: 12px;
            }

            .section-header {
                flex-direction: column;
                align-items: flex-start;
                gap: 12px;
            }

            .section-tools {
                align-self: flex-end;
            }
        }

        /* 全屏样式 */
        .fullscreen {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 1000;
            border-radius: 0;
            animation: none;
        }

        .fullscreen .section-header {
            border-radius: 0;
        }

        .fullscreen textarea,
        .fullscreen #formattedOutput {
            min-height: calc(100vh - 100px);
            max-height: none;
            border-radius: 0;
            border: none;
        }

        .notification {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 15px 25px;
            background: var(--primary-color);
            color: white;
            border-radius: 8px;
            box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
            z-index: 2000;
            transform: translateX(120%);
            transition: transform 0.3s ease;
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .notification.show {
            transform: translateX(0);
        }

        .notification.success {
            background: var(--secondary-color);
        }

        .notification.error {
            background: var(--danger-color);
        }
    </style>
</head>
<body>
<div class="container">
    <header>
        <h1><i class="fas fa-code"></i> XML格式化与压缩工具</h1>
        <p class="subtitle">输入XML内容，获取格式化的、可折叠的XML结构。支持压缩XML、恢复原始格式、复制内容以及全屏查看功能。</p>
    </header>

    <div class="panel-container">
        <div class="input-section" id="inputSection">
            <div class="section-header">
                <span><i class="fas fa-edit"></i> XML输入</span>
                <div class="section-tools">
                    <button class="tool-btn" id="copyInputBtn" title="复制输入内容">
                        <i class="fas fa-copy"></i>
                    </button>
                    <button class="tool-btn" id="fullscreenInputBtn" title="全屏模式">
                        <i class="fas fa-expand"></i>
                    </button>
                </div>
            </div>
            <div class="input-area">
                    <textarea id="xmlInput" placeholder="在此输入XML内容..."><library name="City Public Library">
    <!-- 图书馆信息 -->
    <info established="1985-04-12" location="Main Street 123" squareMeters="1200">
        <contact phone="+1-555-1234" email="info@citylibrary.org" website="www.citylibrary.org"/>
    </info>

    <!-- 图书列表 -->
    <books>
        <book id="B001" category="Fiction" language="English" published="2020-05-15" isbn="978-3-16-148410-0">
            <title>The Great Adventure</title>
            <author>John Smith</author>
            <price currency="USD">19.99</price>
            <description>An epic journey through unknown lands filled with danger and discovery. This book has received critical acclaim and won several awards.</description>
        </book>
        <book id="B002" category="Science" language="English" published="2019-11-22" isbn="978-1-23-456789-0">
            <title>Science Explained</title>
            <author>Marie Dubois</author>
            <price currency="EUR">24.50</price>
            <description>A comprehensive guide to modern scientific discoveries and their implications for humanity. This edition includes recent breakthroughs in quantum computing.</description>
        </book>
        <book id="B003" category="Fantasy" language="English" published="2021-02-18" isbn="978-0-12-345678-9">
            <title>Dragons of Eldoria</title>
            <author>Robert Dragonheart</author>
            <price currency="GBP">15.75</price>
            <description>In the magical kingdom of Eldoria, dragons and humans coexist in harmony until dark forces threaten their peace. The first book in an epic fantasy trilogy.</description>
        </book>
    </books>
</library></textarea>
                <div class="controls">
                    <button class="action-btn" id="formatBtn">
                        <i class="fas fa-magic"></i> 格式化XML
                    </button>
                    <button class="action-btn" id="compressBtn">
                        <i class="fas fa-compress-alt"></i> 压缩XML
                    </button>
                    <button class="action-btn" id="restoreBtn">
                        <i class="fas fa-history"></i> 恢复原始格式
                    </button>
                    <button class="action-btn" id="clearBtn">
                        <i class="fas fa-trash-alt"></i> 清除内容
                    </button>
                </div>
            </div>
        </div>

        <div class="output-section" id="outputSection">
            <div class="section-header">
                <span><i class="fas fa-file-code"></i> 格式化结果</span>
                <div class="section-tools">
                    <button class="tool-btn" id="copyOutputBtn" title="复制格式化结果">
                        <i class="fas fa-copy"></i>
                    </button>
                    <button class="tool-btn" id="fullscreenOutputBtn" title="全屏模式">
                        <i class="fas fa-expand"></i>
                    </button>
                </div>
            </div>
            <div class="output-area">
                <div id="formattedOutput"></div>
            </div>
        </div>
    </div>

    <div class="footer">
        <p>© 2023 XML格式化与压缩工具 | 支持HTML5标准 | 响应式设计 | <a href="#" id="resetLink">重置页面</a></p>
    </div>
</div>

<div class="notification" id="notification">
    <i class="fas fa-check-circle"></i>
    <span id="notificationText">操作成功完成</span>
</div>

<script>
    document.addEventListener('DOMContentLoaded', function() {
        // DOM元素
        const xmlInput = document.getElementById('xmlInput');
        const formattedOutput = document.getElementById('formattedOutput');
        const formatBtn = document.getElementById('formatBtn');
        const compressBtn = document.getElementById('compressBtn');
        const restoreBtn = document.getElementById('restoreBtn');
        const clearBtn = document.getElementById('clearBtn');
        const copyInputBtn = document.getElementById('copyInputBtn');
        const copyOutputBtn = document.getElementById('copyOutputBtn');
        const fullscreenInputBtn = document.getElementById('fullscreenInputBtn');
        const fullscreenOutputBtn = document.getElementById('fullscreenOutputBtn');
        const resetLink = document.getElementById('resetLink');
        const notification = document.getElementById('notification');
        const notificationText = document.getElementById('notificationText');
        const inputSection = document.getElementById('inputSection');
        const outputSection = document.getElementById('outputSection');

        // 状态变量
        let originalInput = '';
        let isInputFullscreen = false;
        let isOutputFullscreen = false;

        // 初始化
        formatXML();

        // 格式化XML
        formatBtn.addEventListener('click', function() {
            formatXML();
            showNotification('XML格式化成功!', 'success');
        });

        // 压缩XML
        compressBtn.addEventListener('click', function() {
            originalInput = xmlInput.value;
            const compressed = compressXML(xmlInput.value);
            xmlInput.value = compressed;
            formattedOutput.textContent = compressed;
            showNotification('XML压缩成功!', 'success');
        });

        // 恢复原始格式
        restoreBtn.addEventListener('click', function() {
            if (originalInput) {
                xmlInput.value = originalInput;
                formatXML();
                showNotification('原始格式已恢复!', 'success');
            } else {
                showNotification('没有可恢复的内容', 'error');
            }
        });

        // 清除内容
        clearBtn.addEventListener('click', function() {
            xmlInput.value = '';
            formattedOutput.innerHTML = '';
            originalInput = '';
            showNotification('内容已清除!', 'success');
        });

        // 复制输入内容
        copyInputBtn.addEventListener('click', function() {
            copyToClipboard(xmlInput.value);
            showNotification('输入内容已复制!', 'success');
        });

        // 复制输出内容
        copyOutputBtn.addEventListener('click', function() {
            copyToClipboard(formattedOutput.textContent);
            showNotification('格式化结果已复制!', 'success');
        });

        // 输入区域全屏
        fullscreenInputBtn.addEventListener('click', function() {
            toggleFullscreen(inputSection, 'input');
        });

        // 输出区域全屏
        fullscreenOutputBtn.addEventListener('click', function() {
            toggleFullscreen(outputSection, 'output');
        });

        // 重置页面
        resetLink.addEventListener('click', function(e) {
            e.preventDefault();
            location.reload();
        });

        // XML格式化函数
        function formatXML() {
            const xmlString = xmlInput.value.trim();
            if (!xmlString) {
                formattedOutput.innerHTML = '<div class="text-content">请输入XML内容进行格式化...</div>';
                return;
            }

            try {
                // 使用DOMParser解析XML
                const parser = new DOMParser();
                const xmlDoc = parser.parseFromString(xmlString, 'text/xml');

                // 检查解析错误
                const parserError = xmlDoc.querySelector('parsererror');
                if (parserError) {
                    throw new Error('XML解析错误: ' + parserError.textContent);
                }

                // 格式化输出
                formattedOutput.innerHTML = '';
                const formattedElement = formatNode(xmlDoc.documentElement, 0);
                formattedOutput.appendChild(formattedElement);

                // 添加折叠功能
                addCollapseFunctionality();
            } catch (error) {
                formattedOutput.innerHTML = `<div class="text-content" style="color:#c0392b;"><i class="fas fa-exclamation-circle"></i> 错误: ${error.message}</div>`;
            }
        }

        // 递归格式化节点
        function formatNode(node, indentLevel) {
            const element = document.createElement('div');
            element.classList.add('element');

            // 处理元素节点
            if (node.nodeType === Node.ELEMENT_NODE) {
                // 检查是否有子节点（除文本节点外）
                const hasChildren = Array.from(node.childNodes).some(
                    child => child.nodeType !== Node.TEXT_NODE ||
                            (child.nodeType === Node.TEXT_NODE && child.textContent.trim() !== '')
                );

                // 创建标签开始部分
                const tagStart = document.createElement('span');
                tagStart.classList.add('tag');
                tagStart.innerHTML = '&lt;' + node.nodeName;

                // 添加属性
                if (node.attributes.length > 0) {
                    const attributesContainer = document.createElement('div');
                    attributesContainer.style.display = 'inline';

                    for (let i = 0; i < node.attributes.length; i++) {
                        const attr = node.attributes[i];

                        const attrSpan = document.createElement('span');
                        attrSpan.innerHTML = ` <span class="attribute-name">${attr.name}</span>="<span class="attribute-value">${attr.value}</span>"`;

                        // 在属性过多时添加换行
                        if (i > 0) {
                            attrSpan.innerHTML = '<br/>' + '&nbsp;'.repeat(indentLevel * 4 + 4) + attrSpan.innerHTML;
                        }

                        attributesContainer.appendChild(attrSpan);
                    }

                    tagStart.appendChild(attributesContainer);
                }

                tagStart.innerHTML += '&gt;';

                // 处理文本内容
                const textNodes = Array.from(node.childNodes).filter(
                    child => child.nodeType === Node.TEXT_NODE && child.textContent.trim() !== ''
                );

                if (textNodes.length === 1 && !hasChildren) {
                    // 只有文本内容，没有子元素
                    const textSpan = document.createElement('span');
                    textSpan.classList.add('text-content');
                    textSpan.textContent = textNodes[0].textContent.trim();

                    tagStart.appendChild(textSpan);

                    // 添加结束标签
                    const tagEnd = document.createElement('span');
                    tagEnd.classList.add('tag');
                    tagEnd.innerHTML = `&lt;/${node.nodeName}&gt;`;

                    // 包装成单行元素
                    const singleLine = document.createElement('div');
                    singleLine.classList.add('single-line-element');
                    singleLine.appendChild(tagStart);
                    singleLine.appendChild(tagEnd);

                    element.appendChild(singleLine);
                } else if (hasChildren) {
                    // 有子元素
                    element.appendChild(tagStart);

                    // 添加子节点
                    for (const childNode of node.childNodes) {
                        if (childNode.nodeType === Node.TEXT_NODE && childNode.textContent.trim() === '') {
                            continue; // 忽略空白文本节点
                        }

                        const childElement = formatNode(childNode, indentLevel + 1);
                        element.appendChild(childElement);
                    }

                    // 添加结束标签
                    const tagEnd = document.createElement('div');
                    tagEnd.classList.add('tag');
                    tagEnd.style.marginLeft = (indentLevel * 20) + 'px';
                    tagEnd.innerHTML = `&lt;/${node.nodeName}&gt;`;

                    element.appendChild(tagEnd);

                    // 添加折叠功能
                    element.classList.add('collapsible');
                } else {
                    // 自闭合标签
                    element.appendChild(tagStart);
                }
            }
            // 处理注释节点
            else if (node.nodeType === Node.COMMENT_NODE) {
                element.classList.add('comment');
                element.textContent = `<!--${node.textContent}-->`;
                element.style.marginLeft = (indentLevel * 20) + 'px';
            }
            // 处理文本节点
            else if (node.nodeType === Node.TEXT_NODE) {
                const text = node.textContent.trim();
                if (text) {
                    element.classList.add('text-content');
                    element.textContent = text;
                    element.style.marginLeft = (indentLevel * 20) + 'px';
                }
            }

            return element;
        }

        // 添加折叠功能
        function addCollapseFunctionality() {
            const collapsibleElements = document.querySelectorAll('.element.collapsible');

            collapsibleElements.forEach(el => {
                el.addEventListener('click', function(e) {
                    // 防止点击标签时触发折叠
                    if (e.target.classList.contains('tag') ||
                        e.target.classList.contains('attribute-name') ||
                        e.target.classList.contains('attribute-value') ||
                        e.target.classList.contains('text-content')) {
                        return;
                    }

                    this.classList.toggle('collapsed');

                    // 切换子元素的显示/隐藏
                    const children = this.querySelectorAll('.element');
                    children.forEach(child => {
                        if (child !== this.firstElementChild && child !== this.lastElementChild) {
                            child.style.display = this.classList.contains('collapsed') ? 'none' : 'block';
                        }
                    });
                });
            });
        }

        // XML压缩函数
        function compressXML(xmlString) {
            return xmlString
                .replace(/<!--[\s\S]*?-->/g, '') // 移除注释
                .replace(/>\s+</g, '><')        // 移除标签间的空白
                .replace(/\s+/g, ' ')            // 将多个空格替换为一个
                .replace(/\s+$/gm, '')           // 移除行尾空格
                .trim();
        }

        // 复制到剪贴板
        function copyToClipboard(text) {
            const textarea = document.createElement('textarea');
            textarea.value = text;
            document.body.appendChild(textarea);
            textarea.select();
            document.execCommand('copy');
            document.body.removeChild(textarea);
        }

        // 显示通知
        function showNotification(message, type = '') {
            notificationText.textContent = message;
            notification.className = 'notification';
            notification.classList.add('show', type);

            setTimeout(() => {
                notification.classList.remove('show');
            }, 3000);
        }

        // 切换全屏
        function toggleFullscreen(element, type) {
            if ((type === 'input' && !isInputFullscreen) ||
                (type === 'output' && !isOutputFullscreen)) {

                element.classList.add('fullscreen');

                if (type === 'input') {
                    isInputFullscreen = true;
                    fullscreenInputBtn.innerHTML = '<i class="fas fa-compress"></i>';
                    fullscreenInputBtn.title = '退出全屏';
                } else {
                    isOutputFullscreen = true;
                    fullscreenOutputBtn.innerHTML = '<i class="fas fa-compress"></i>';
                    fullscreenOutputBtn.title = '退出全屏';
                }
            } else {
                element.classList.remove('fullscreen');

                if (type === 'input') {
                    isInputFullscreen = false;
                    fullscreenInputBtn.innerHTML = '<i class="fas fa-expand"></i>';
                    fullscreenInputBtn.title = '全屏模式';
                } else {
                    isOutputFullscreen = false;
                    fullscreenOutputBtn.innerHTML = '<i class="fas fa-expand"></i>';
                    fullscreenOutputBtn.title = '全屏模式';
                }
            }
        }
    });
</script>
</body>
</html>
